//
// Copyright 2012-2013, Syoyo Fujita.
//
// Licensed under 2-clause BSD liecense.
//
// Modified by Sun Wanjie Nantong university

#ifndef _TINY_OBJ_LOADER_H
#define _TINY_OBJ_LOADER_H

#include <string>
#include <vector>
#include <map>

#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/GLUT.h>
#else
#include <GL/glew.h>
#include <GL/glut.h>
#endif

namespace tinyobj {

typedef struct
{
    std::string name;

    float ambient[3];
    float diffuse[3];
    float specular[3];
    float transmittance[3];
    float emission[3];
    float shininess;
    float ior;                // index of refraction
    float dissolve;           // 1 == opaque; 0 == fully transparent
    // illumination model (see http://www.fileformat.info/format/material/)
    int illum;

    std::string ambient_texname;
    std::string diffuse_texname;
    std::string specular_texname;
    std::string normal_texname;
    std::map<std::string, std::string> unknown_parameter;
} material_t;

typedef struct
{
    std::vector<float>          positions;
    std::vector<float>          normals;
    std::vector<float>          texcoords;
    std::vector<unsigned int>   indices;
} mesh_t;

typedef struct
{
    std::string  name;
    material_t   material;
    mesh_t       mesh;
    GLuint VAO;
    GLuint VBO[4];
    GLuint diffuseTex;
    GLuint specularTex;
    GLuint ambientTex;
    GLuint normalTex;
} shape_t;

class MaterialReader
{
public:
    MaterialReader(){}
    virtual ~MaterialReader(){}

    virtual std::string operator() (
        const std::string& matId,
        std::map<std::string, material_t>& matMap) = 0;
};

class MaterialFileReader:
  public MaterialReader
{
    public:
        MaterialFileReader(const std::string& mtl_basepath): m_mtlBasePath(mtl_basepath) {}
        virtual ~MaterialFileReader() {}
        virtual std::string operator() (
          const std::string& matId,
          std::map<std::string, material_t>& matMap);

    private:
        std::string m_mtlBasePath;
};

/// Loads .obj from a file.
/// 'shapes' will be filled with parsed shape data
/// The function returns error string.
/// Returns empty string when loading .obj success.
/// 'mtl_basepath' is optional, and used for base path for .mtl file.
std::string LoadObj(
    std::vector<shape_t>& shapes,   // [output]
    const char* filename,
    const char* mtl_basepath = NULL);

/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve
/// std::istream for materials.
/// Returns empty string when loading .obj success.
std::string LoadObj(
    std::vector<shape_t>& shapes,   // [output]
    std::istream& inStream,
    MaterialReader& readMatFn);

/// Loads materials into std::map
/// Returns an empty string if successful
std::string LoadMtl (
  std::map<std::string, material_t>& material_map,
  std::istream& inStream);
};

/********************************************************************************************/
/*                                                                                          */
/*                                  Composed by Sun Wanjie                                  */
/*                                                                                          */
/********************************************************************************************/

#include "texture.h"

class ObjModel
{
protected:
    std::string filePath;
    bool hasModel;
    
    //obj model data
    std::vector<tinyobj::shape_t> shapes;
    float BBMax[3], BBMin[3];
    float center[3];
    float radius;
    float width, height, depth;
    //offset to the origin
    float offsetX, offsetY, offsetZ;
    //normalize factor
    float scaleFactor;
    
    GLuint displayList;
    
protected:
    void Init();
    void ComputeBoundingBox();
    void ComputeVertexNormal(tinyobj::shape_t &);
    void NormalAveraging(tinyobj::shape_t &);

public:
    enum display_mode {SOLID, WIREFRAME, SOLID_WIREFRAME};
    
public:
    ObjModel();
    ObjModel(std::string filename);
    ~ObjModel();
    void Read(std::string filename);
    void Draw(display_mode mode);
    void VAODraw(display_mode mode);
    void PrepareVAO();
    
    inline float Radius() {return radius;}
    inline const float* Center() {return center;}
    inline const float* BoundingBoxMaxPt() {return BBMax;}
    inline const float* BoudingBoxMinPt() {return BBMin;}
    inline float Width() {return width;}
    inline float Height() {return height;}
    inline float Depth() {return depth;}
    inline float ScaleFactor() {return scaleFactor;}
    inline float XOffset() {return offsetX;}
    inline float YOffset() {return offsetY;}
    inline float ZOffset() {return offsetZ;}
};


#endif  // _TINY_OBJ_LOADER_H
